{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Updating a Historical Marquee Portfolio\n",
    "\n",
    "If you already have a portfolio in Marquee, the GS Quant SDK provides a simple and intuitive workflow to update positions and rerun reports.\n",
    "\n",
    "## Permission Prerequisites\n",
    "\n",
    "To execute all the code in this tutorial, you will need the following application scopes:\n",
    "- **read_product_data**\n",
    "- **read_financial_data**\n",
    "- **modify_financial_data** (must be requested)\n",
    "- **run_analytics** (must be requested)\n",
    "\n",
    "If you are not yet permissioned for these scopes, please request them on your [My Applications Page](https://developer.gs.com/go/apps/view). If you have any other questions please reach out to the [Marquee sales team](mailto:gs-marquee-sales@gs.com).\n",
    "\n",
    "You will also need to be an admin on the portfolio you would like to update. If you are not an admin, please ask a portfolio admin to [edit the portfolio's entitlements](../examples/marquee/01_edit_portfolio_entitlements.ipynb) to include you."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 1: Authenticate and Initialize Your Session\n",
    "\n",
    "First you will import the necessary modules and add your client id and client secret."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "import itertools\n",
    "from time import sleep\n",
    "\n",
    "from gs_quant.api.gs.assets import GsAssetApi\n",
    "from gs_quant.api.gs.portfolios import GsPortfolioApi\n",
    "from gs_quant.common import PositionSet\n",
    "from gs_quant.markets.portfolio_manager import PortfolioManager\n",
    "from gs_quant.session import GsSession, Environment\n",
    "from gs_quant.target.portfolios import Position\n",
    "\n",
    "client = 'ENTER CLIENT ID'\n",
    "secret = 'ENTER CLIENT SECRET'\n",
    "\n",
    "\n",
    "GsSession.use(Environment.PROD, client_id=client, client_secret=secret, scopes=('read_product_data read_financial_data modify_financial_data run_analytics',))\n",
    "\n",
    "print('GS Session initialized.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 2: Define Your Portfolio ID and the Positions You Would Like to Upload\n",
    "\n",
    "Portfolio positions in Marquee are stored on a holding basis, when means you only upload positions for days where you are rebalancing your portfolio. Take the following set of positions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "portfolio_id = 'ENTER PORTFOLIO ID'\n",
    "positions = [\n",
    "    { 'identifier': 'GS UN', 'quantity': 50, 'positionDate': '2020-05-01'},\n",
    "    { 'identifier': 'AAPL UW', 'quantity': 25, 'positionDate': '2020-05-01'},\n",
    "    { 'identifier': 'GS UN', 'quantity': 51, 'positionDate': '2020-07-01'},\n",
    "    { 'identifier': 'AAPL UW', 'quantity': 26, 'positionDate': '2020-07-01'}\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If these positions were to be uploaded correctly, that portfolio would hold 50 shares of GS UN and 25 shares of AAPL UW from May 1, 2020 to June 30, 2020, and it would hold 51 shares of GS UN and 26 shares of AAPL UW from July 1, 2020 to today (assuming the portfolio was initially empty).\n",
    "\n",
    "## Step 3: Format positions\n",
    "\n",
    "Now let's proceed with updating these positions to our portfolio. The first step is to resolve the identifiers provided into their corresponding unique Marquee identifiers. In this case, positions are identified by Bloomberg ID, but other identifiers can be used and resolved by adding them to the `fields` parameter in the function `GsAssetApi.resolve_assets`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "all_identifiers = list(set([p['identifier'] for p in positions]))\n",
    "results = GsAssetApi.resolve_assets(identifier=all_identifiers, fields=['bbid', 'id'], limit=1)\n",
    "try:\n",
    "    identifier_to_marquee_id = dict(zip(results.keys(), [a[0]['id'] for a in results.values()]))\n",
    "except:\n",
    "    unmapped_assets = {k for k,v in results.items() if not v}\n",
    "    raise ValueError('Error in resolving the following identifiers: ' + ', '.join(unmapped_assets))\n",
    "\n",
    "print('Position identifiers successfully mapped as the following:')\n",
    "for mq_id in identifier_to_marquee_id:\n",
    "    print(f'{mq_id} --> {identifier_to_marquee_id[mq_id]}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next we need to rearrange the data in the positions to fit the format expected in the Marquee API."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "portfolio_position_sets = []\n",
    "\n",
    "for position_date, positions_on_date in itertools.groupby(positions, lambda x: x['positionDate']):\n",
    "    formatted_positions = tuple(Position(asset_id=identifier_to_marquee_id[p['identifier']],\n",
    "                                    quantity=p['quantity']) for p in positions_on_date)\n",
    "    position_set = (PositionSet(position_date=position_date,\n",
    "                                positions=formatted_positions))\n",
    "    portfolio_position_sets.append(position_set)\n",
    "\n",
    "print('Portfolio positions successfully formatted.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 4: Post Positions to the Marquee Portfolio\n",
    "We're finally ready to update the portfolio with the positions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "GsPortfolioApi.update_positions(portfolio_id, portfolio_position_sets)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 5: Rerun All Portfolio Reports\n",
    "\n",
    "Once your portfolio has new positions, it's time to rerun all reports associated with the portfolio. You can do that easily using the `PortfolioManager` class.\n",
    "\n",
    "When running reports, you have the option of running them synchronously (meaning the function `run_reports()` will wait for all reports to finish running to return their results), or asynchronously (meaning the function will return a list of `ReportJobFuture` objects that will store the result of the report job once it is available). This tutorial will leverage the asyncronous feature."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "pm = PortfolioManager(portfolio_id)\n",
    "\n",
    "report_results = pm.run_reports(backcast=False, is_async=True)\n",
    "for future in report_results:\n",
    "    while not future.done():\n",
    "        # you can use this time to perform other calculations or run other functions\n",
    "        sleep(5)\n",
    "    print(f'~~~~~~~~ Results ~~~~~~~~')\n",
    "    report_result = future.result()\n",
    "    print(report_result.__str__())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Quick Tip!\n",
    "If you would only like to run reports on a specified range of dates, pass `start_date` and `end_date` datetime.date objects into the `run_reports()` function parameters.\n",
    "\n",
    "### You're all set, Congrats! What's next?\n",
    "\n",
    "* [Creating and scheduling a new factor risk report](../examples/marquee/00_create_factor_risk_report.ipynb)\n",
    "\n",
    "* [Retrieving the portfolio's performance analytics](../tutorials/Pull%20Portfolio%20Performance%20Data.ipynb)\n",
    "\n",
    "* [Retrieving the portfolio's factor risk and attribution analytics](../tutorials/Pull%20Portfolio%20Factor%20Risk%20Data.ipynb)\n",
    "\n",
    "\n",
    "*Other questions? Reach out to the [Portfolio Analytics team](mailto:gs-marquee-analytics-support@gs.com)!*"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
